:- module(module_options).
:- export print_default_options / 1.
:- export print_default_options / 2.
:- export set_default_option / 3.
:- export set_default_option / 2.
:- export get_options / 3.
:- export get_options / 2.
:- comment(summary, "Utility library to manage options within a library module").
:- comment(author, "Joachim Schimpf").
:- comment(copyright, "Imperial College London and Parc Technologies Ltd").
:- comment(date, "$Date: 2004/12/03 15:28:53 $").
:- comment(desc, html("<P>\n\tThis library provides utilities to manage option settings on behalf\n\tof other library modules. The basic idea is that each client module\n\tcan define what it considers to be valid option names and values, plus\n\tthe structure in terms of which the set of all options will be stored\n\tand returned.\n</P><P>\n\tFor each client library, global default option settings are maintained\n\twhich can be modified (in a non-backtrackable fashion) using\n\tset_default_option/2. Whenever the settings are retrieved, the\n\tdefaults can be individually overridden using a user-supplied option\n\tlist.\n</P><P>\n\tEvery client module has to define 3 local predicates:\n<DL>\n\t<DT>valid_option_field(?Name, -FieldIndex)<DD>\n\t    defines the option names (atoms) and the position (integer)\n\t    of the option value within an option structure.\n\t<DT>valid_option_value(+Name, +Value)<DD>\n\t    defines what constitues a valid option value, by\n\t    provides a type/range check for Value. The predicate should\n\t    fail if Value is not a valid value for option Name.\n\t<DT>default_options(-OptionStructure)<DD>\n\t    this should be a single fact which should return a structure.\n\t    The structure arguments define the initial default settings\n\t    for each option field. The structure's functor defines the\n\t    skeleton in terms of which option settings will be returned\n\t    by get_options/2.\n\t    Note that the structure can have extra fields which are\n\t    not defined as valid options, are not user-modifiable, and\n\t    will therefore always be returned unchanged by get_options/2.\n</DL>\n\tA typical application would in addition define a toplevel predicate\n\tthat accepts a user-supplied option list per invocation, and possibly\n\ta predicate to modify the global default settings.  Sample usage:\n<PRE>\n\t:- module(my_module).\n\n\t:- lib(module_options).\n\n\tvalid_option_field(a, 1).\n\tvalid_option_field(b, 2).\n\tvalid_option_field(c, 3).\n\n\tvalid_option_value(a, Value) :- integer(Value).\n\tvalid_option_value(b, Value) :- atom(Value).\n\tvalid_option_value(c, Value) :- atom(Value).\n\n\tdefault_options(options(23,hello,world,there)).\n\n\t:- export my_set_default_option/2.\n\tmy_set_default_option(Name, Value) :-\n\t    set_default_option(Name, Value).\n\n\t:- export my_predicate/2.\n\tmy_predicate(Arguments, OptionList) :-\n\t    ( get_options(OptionList, OptionStruct) ->\n\t\t...\n\t    ;\n\t\tprintf(error, \"Invalid option list: %w%n\", [OptionList]),\n\t\tprint_default_options(error),\n\t\tabort\n\t    ).\n</PRE>\n\tIn practice, it is recommended to use structure notation for the\n\toption structure for better readability and maintainability, i.e.\n<PRE>\n\t:- module(my_module).\n\n\t:- lib(module_options).\n\n\t:- local struct(options(a,b,c,d)).\n\n\tvalid_option_field(a, a of options).\n\tvalid_option_field(b, b of options).\n\tvalid_option_field(c, c of options).\n\n\tvalid_option_value(a, Value) :- integer(Value).\n\tvalid_option_value(b, Value) :- atom(Value).\n\tvalid_option_value(c, Value) :- atom(Value).\n\n\tdefault_options(options{a:23,b:hello,c:world,d:there}).\n\n\t:- export my_set_default_option/2.\n\tmy_set_default_option(Name, Value) :-\n\t    set_default_option(Name, Value).\n\n\t:- export my_predicate/2.\n\tmy_predicate(Arguments, OptionList) :-\n\t    ( get_options(OptionList, OptionStruct) ->\n\t\t...\n\t    ;\n\t\tprintf(error, \"Invalid option list: %w%n\", [OptionList]),\n\t\tprint_default_options(error),\n\t\tabort\n\t    ).\n</PRE>\n\tIt is not absulotely necessary to define a predicate like\n\tmy_set_default_option/2 since <CODE>my_set_default_option(opt,val)</CODE>\n\tis equivalent to <CODE>set_default_option(opt,val)@my_module</CODE>.\n</P>\n")).
:- comment(get_options / 2, [summary : "Create a structure OptionStruct from OptionList and the context module's default settings", args : ["OptionList" : "List of Name:Value pairs", "OptionStruct" : "Variable, will be bound to a structure"], amode : get_options(+, -), fail_if : "OptionList contains illegal names or values", see_also : [library(module_options), set_default_option / 2, print_default_options / 1], desc : html("<P>\n    The exact behaviour of this predicate depends on the module from where\n    it is invoked: It returns a structure OptionStruct, whose skeleton and\n    default field values are defined by the predicate default_options/1 in\n    the context module. Moreover, OptionList is analysed, and any option\n    values specified there will show up in the corresponding fields of the\n    returned structure OptionStruct, instead of the default values defined\n    by default_options/1.\n</P><P>\n    The option names in OptionList must be valid according to the predicate\n    valid_option_field/2 in the context module, and the corresponding values\n    must be valid according to valid_option_value/2.\n</P>\n    ")]).
:- comment(print_default_options / 1, [summary : "Print the valid options and their current default values to Stream", args : ["Stream" : "An output stream"], amode : print_default_options(+), see_also : [library(module_options), get_options / 2, set_default_option / 2], desc : html("<P>\n    The exact behaviour of this predicate depends on the module from where\n    it is invoked: It prints a list of the valid options and their current\n    default values onto the stream Stream.\n</P>\n    ")]).
:- comment(set_default_option / 2, [summary : "Permanently set the default value for the given option in the context module", args : ["OptionName" : "An atom", "OptionValue" : "A valid option term"], amode : set_default_option(+, +), exceptions : [4 : "OptionName or OptionValue are uninstantiated", 5 : "OptionName is not an atom", 6 : "OptionName is not a valid option, or OptionValue is not a valid value in the context module"], see_also : [library(module_options), get_options / 2, print_default_options / 1], desc : html("<P>\n    The exact behaviour of this predicate depends on the module from where\n    it is invoked: It changes the default value for a named option in the\n    context module. The initial default values are defined by the predicate\n    default_options/1 in the context module.\n</P><P>\n    The option name OptionName must be valid according to the predicate\n    valid_option_field/2 in the context module, and OptionValue\n    must be valid according to valid_option_value/2.\n</P>\n    ")]).
:- comment(print_default_options / 2, hidden).
:- comment(set_default_option / 3, hidden).
:- comment(get_options / 3, hidden).
